package org.syntaxlisp.core.utils;

import org.syntaxlisp.core.ParseException;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author HuangYijun
 * @data 2023-02-13 11:50
 **/
public class CommonUtils {

    public static void checkOrThrow(BooleanSupplier checkCond, String msg) {
        checkOrThrow(checkCond.getAsBoolean(), msg);
    }

    public static void checkOrThrow(boolean condRes, String msg) {
        if (!condRes) {
            throw new ParseException(msg);
        }
    }

    public static void checkOrThrow (boolean condRes, Supplier<RuntimeException> expSupplier) {
        if (!condRes) {
            throw expSupplier.get();
        }
    }

    public static <T> boolean contains (Collection<T> collection, Predicate<T> cond) {
        for (T ele : collection) {
            if (cond.test(ele)) { return true; }
        }
        return false;
    }

    public static <K,V> Map<K, V> collect (Map<K,V> srcMap, BiPredicate<K,V> cond) {
        Map<K,V> col = new HashMap<>();
        srcMap.forEach((key, val) -> {
            if (cond.test(key, val)) {
                col.put(key, val);
            }
        });
        return col;
    }

    public static <T> List<T> asWritableList(T ... data) {
        return new ArrayList<>(Arrays.asList(data));
    }

    public static <T> List<T> asWritableList(Collection<T> src, T ... data) {
        List<T> newList = new ArrayList<>(src);
        for (T add : data) {
            newList.add(add);
        }
        return newList;
    }

    public static <T> Set<T> asWritableSet(T ... data) {
        return new HashSet<>(Arrays.asList(data));
    }


    public static <T> List<T> singletonList (T single) {
        return new ArrayList<>(Collections.singletonList(single));
    }

    public static boolean equals (Object a, Object b) {
        return (a == null && b == null)
               || (a != null && a.equals(b));
    }
    public static boolean equalsTo(EqualsTo a, EqualsTo b) {
        return (a == null && b == null)
                || (a != null && a.equals(b));
    }

    public static <T extends EqualsTo> boolean equalsTo(List<T> al, List<T> bl) {
        if (!CollectionUtils.isEmpty(al) && !CollectionUtils.isEmpty(bl) && al.size() == bl.size()) {
            for (T a : al) {
                if (!bl.stream().anyMatch(e -> e.equalsTo(a))) {
                    return false;
                }
            }
            return true;
        } else {
            return al == null && bl == null;
        }
    }

    public static <T> void pop (List<T> stack) {
        if (!CollectionUtils.isEmpty(stack)) {
            stack.remove(stack.size() - 1);
        }
    }

    @SafeVarargs
    public static <T> void push (List<T> stack, T ... seq) {
        stack.addAll(Arrays.asList(seq));
    }

    public static <T> Optional<T> top (List<T> stack) {
        return !CollectionUtils.isEmpty(stack)
               ? Optional.of(stack.get(stack.size() - 1))
               : Optional.empty();
    }

    public static <T extends Copyable<T>> List<T> copyStack(List<T> src) {
        return src.stream().map(Copyable::copy).collect(Collectors.toList());
    }

    public static boolean getOrElseAnd (Boolean original, boolean val) {
        return original == null
               ? val
               : (original && val);
    }

    public static boolean getOrElseFalse (Boolean val) {
        return KBeanUtils.getOrDefault(val, false);
    }
}
